<template>
  <view class="s-animation" :class="c_class" :style="c_style">
    <slot />
  </view>
</template>

<script>
import componentMixin from '../../mixins/componentMixin';

/**
 * s-animation 动画
 * @description 用来控制一个元素显示隐藏时的动画
 * @property {Boolean} value v-model双向绑定显示隐藏
 * @property {String|Object} name = [custom|fade|fade-top|slide-top|{enter:'fade',leave:'fade-top'}] 过渡效果 ，为s-ui styles下animate.scss中定义的效果
 * @property {Number|String|Object} duration 动画执行时间
 * @property {String} enterClass 自定义动画时，显示时class
 * @property {String} leaveClass 自定义动画时，隐藏时class
 * @event {Function} before-enter 显示之前触发
 * @event {Function} enter 显示之后触发
 * @event {Function} before-leave 隐藏之前触发
 * @event {Function} leave 隐藏之后触发
 * @example <s-animation v-model="visible"></s-animation>
 */
export default {
  name: 'SAnimation',
  mixins: [componentMixin],
  props: {
    value: Boolean,
    name: {
      type: [String, Object],
      default: 'fade',
    },
    duration: {
      type: [Number, String, Object],
      default: 300,
    },
    enterClass: {
      type: String,
      default: 's-animate-enter',
    },
    leaveClass: {
      type: String,
      default: 's-animate-leave',
    },
  },
  data: () => ({
    isVisible: false,
    animateStatus: '',
  }),
  computed: {
    c_class() {
      const classList = [];
      const animateStatus = this.animateStatus;
      if (animateStatus) {
        const animateStatusName = this.animateNameMap[animateStatus];
        if (animateStatusName) {
          classList.push(`s-animate-${animateStatusName}-${animateStatus}`);
        }

        const animateStatusClass = this[`${animateStatus}Class`];
        if (animateStatusClass) {
          classList.push(animateStatusClass);
        }
      }
      return this.$mergeClass(classList, this.custom_class);
    },
    c_style() {
      return this.$mergeStyle({
        animationDuration: this.animateStatus ? this.animateDurationMap[this.animateStatus] + 'ms' : '',
        display: this.isVisible ? '' : 'none',
      }, this.custom_style);
    },
    animateNameMap() {
      let name = this.name;
      if (name && name !== 'custom') {
        if (this.$getType(name) !== 'object') name = { enter: name, leave: name };
        return { enter: name.enter || '', leave: name.leave || '' };
      }
      return { enter: '', leave: '' };
    },
    animateDurationMap() {
      let duration = this.duration;
      if (this.$getType(duration) !== 'object') duration = { enter: duration, leave: duration };
      return { enter: parseInt(duration.enter) || 0, leave: parseInt(duration.leave) || 0 };
    },
  },
  watch: {
    value() {
      this.updateVisible();
    },
  },
  created() {
    Object.assign(this, {
      visibleStatus: false,
      animateTimer: 0,
    });
  },
  mounted() {
    this.updateVisible();
  },
  beforeDestroy() {
    clearTimeout(this.animateTimer);
  },
  methods: {
    show() {
      if (!this.visibleStatus) {
        clearTimeout(this.animateTimer);
        this.visibleStatus = true;
        this.isVisible = true;
        this.animateStatus = 'enter';
        this.$emit('input', true);
        this.$emit('before-enter');
        this.animateTimer = setTimeout(() => {
          this.animateStatus = '';
          this.$emit('enter');
        }, this.animateDurationMap.enter);
      }
    },
    hide() {
      if (this.visibleStatus) {
        clearTimeout(this.animateTimer);
        this.visibleStatus = false;
        this.animateStatus = 'leave';
        this.$emit('input', false);
        this.$emit('before-leave');
        this.animateTimer = setTimeout(() => {
          this.isVisible = false;
          this.animateStatus = '';
          this.$emit('leave');
        }, this.animateDurationMap.leave);
      }
    },
    updateVisible() {
      if (this._isMounted && this.visibleStatus !== this.value) {
        this[this.value ? 'show' : 'hide']();
      }
    },
  },
};
</script>

<style lang="scss" src="./index.scss"></style>
